import { createSelector } from "reselect";
import type { DefaultRootState } from "react-redux";
import type {
  CanvasWidgetsReduxState,
  FlattenedWidgetProps,
} from "ee/reducers/entityReducers/canvasWidgetsReducer";
import { getExistingWidgetNames } from "sagas/selectors";
import { getNextEntityName } from "utils/AppsmithUtils";

import WidgetFactory from "WidgetProvider/factory";
import { getWidgetConfigsVersion } from "WidgetProvider/factory/widgetConfigVersion";
import {
  getAltBlockWidgetSelection,
  getFocusedWidget,
  getLastSelectedWidget,
  getSelectedWidgets,
} from "./ui";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
import { get } from "lodash";
import { getAppMode } from "ee/selectors/applicationSelectors";
import { APP_MODE } from "entities/App";
import { getIsTableFilterPaneVisible } from "selectors/tableFilterSelectors";
import { getIsAutoHeightWithLimitsChanging } from "utils/hooks/autoHeightUIHooks";
import { getIsPropertyPaneVisible } from "./propertyPaneSelectors";
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
import { selectCombinedPreviewMode } from "./gitModSelectors";

export const getIsDraggingOrResizing = (state: DefaultRootState) =>
  state.ui.widgetDragResize.isResizing || state.ui.widgetDragResize.isDragging;

export const getIsResizing = (state: DefaultRootState) =>
  state.ui.widgetDragResize.isResizing;

const getCanvasWidgets = (state: DefaultRootState) =>
  state.entities.canvasWidgets;

// A selector that gets the modal widget type based on the feature flag
// This will need to be updated once Anvil and WDS are generally available
export const getModalWidgetType = createSelector(
  getIsAnvilLayout,
  (isAnvilLayout: boolean) => {
    let modalWidgetType = "MODAL_WIDGET";

    if (isAnvilLayout) {
      modalWidgetType = "WDS_MODAL_WIDGET";
    }

    return modalWidgetType;
  },
);

export const getModalWidgets = createSelector(
  getCanvasWidgets,
  getModalWidgetType,
  (widgets, modalWidgetType) => {
    const modalWidgets = Object.values(widgets).filter(
      (widget: FlattenedWidgetProps) => widget.type === modalWidgetType,
    );

    if (modalWidgets.length === 0) return undefined;

    return modalWidgets;
  },
);

export const getModalDropdownList = createSelector(
  getModalWidgets,
  (modalWidgets) => {
    if (!modalWidgets) return undefined;

    return modalWidgets.map((widget: FlattenedWidgetProps) => ({
      id: widget.widgetId,
      label: widget.widgetName,
      value: `${widget.widgetName}.name`,
    }));
  },
);

export const getNextModalName = createSelector(
  getExistingWidgetNames,
  getModalWidgetType,
  getWidgetConfigsVersion, // Add dependency on widget configs version
  (names, modalWidgetType) => {
    const prefix =
      WidgetFactory.widgetConfigMap.get(modalWidgetType)?.widgetName || "";

    return getNextEntityName(prefix, names);
  },
);

/**
 * Selector to get the parent widget of a particaular widget with id as a prop
 */
export const getParentWidget = createSelector(
  getCanvasWidgets,
  (state: DefaultRootState, widgetId: string) => widgetId,
  (canvasWidgets, widgetId: string): FlattenedWidgetProps | undefined => {
    if (canvasWidgets.hasOwnProperty(widgetId)) {
      const widget = canvasWidgets[widgetId];

      if (widget.parentId && canvasWidgets.hasOwnProperty(widget.parentId)) {
        const parent = canvasWidgets[widget.parentId];

        return parent;
      }
    }

    return;
  },
);

export const getFocusedParentToOpen = createSelector(
  getCanvasWidgets,
  (state: DefaultRootState) => state.ui.widgetDragResize.focusedWidget,
  (canvasWidgets, focusedWidgetId) => {
    return getParentToOpenIfAny(focusedWidgetId, canvasWidgets);
  },
);

export const getParentToOpenSelector = (widgetId: string) => {
  return createSelector(getCanvasWidgets, (canvasWidgets) => {
    return getParentToOpenIfAny(widgetId, canvasWidgets);
  });
};

// Check if widget is in the list of selected widgets
export const isWidgetSelected = (widgetId?: string) => {
  return createSelector(getSelectedWidgets, (widgets): boolean =>
    widgetId ? widgets.includes(widgetId) : false,
  );
};

export const isWidgetFocused = (widgetId: string) => {
  return createSelector(
    getFocusedWidget,
    (widget): boolean => widget === widgetId,
  );
};

// Check if current widget is the last selected widget
export const isCurrentWidgetLastSelected = (widgetId: string) => {
  return createSelector(
    getLastSelectedWidget,
    (widget): boolean => widget === widgetId,
  );
};

// Check if current widget is one of multiple selected widgets
export const isMultiSelectedWidget = (widgetId: string) => {
  return createSelector(
    getSelectedWidgets,
    (widgets): boolean => widgets.length > 1 && widgets.includes(widgetId),
  );
};

export function getParentToOpenIfAny(
  widgetId: string | undefined,
  widgets: CanvasWidgetsReduxState,
) {
  if (widgetId) {
    let widget = get(widgets, widgetId, undefined);

    // While this widget has a openParentPropertyPane equal to true
    while (widget?.openParentPropertyPane) {
      // Get parent widget props
      const parent = get(widgets, `${widget.parentId}`, undefined);

      // If parent has openParentPropertyPane = false, return the current parent
      if (!parent?.openParentPropertyPane) {
        return parent;
      }

      if (parent?.parentId && parent.parentId !== MAIN_CONTAINER_WIDGET_ID) {
        widget = get(widgets, `${widget.parentId}`, undefined);

        continue;
      }
    }
  }

  return;
}

export const shouldWidgetIgnoreClicksSelector = (widgetId: string) => {
  return createSelector(
    getFocusedWidget,
    getIsTableFilterPaneVisible,
    (state: DefaultRootState) => state.ui.widgetDragResize.isResizing,
    (state: DefaultRootState) => state.ui.widgetDragResize.isDragging,
    (state: DefaultRootState) =>
      state.ui.canvasSelection.isDraggingForSelection,
    getAppMode,
    selectCombinedPreviewMode,
    getIsAutoHeightWithLimitsChanging,
    getAltBlockWidgetSelection,
    (
      focusedWidgetId,
      isTableFilterPaneVisible,
      isResizing,
      isDragging,
      isDraggingForSelection,
      appMode,
      isPreviewMode,
      isAutoHeightWithLimitsChanging,
      isWidgetSelectionBlock,
    ) => {
      const isFocused = focusedWidgetId === widgetId;

      return (
        isDraggingForSelection ||
        isResizing ||
        isDragging ||
        isPreviewMode ||
        appMode !== APP_MODE.EDIT ||
        !isFocused ||
        isTableFilterPaneVisible ||
        isAutoHeightWithLimitsChanging ||
        isWidgetSelectionBlock
      );
    },
  );
};

export const getSelectedWidgetAncestry = (state: DefaultRootState) =>
  state.ui.widgetDragResize.selectedWidgetAncestry;

export const getEntityExplorerWidgetAncestry = (state: DefaultRootState) =>
  state.ui.widgetDragResize.entityExplorerAncestry;

export const getEntityExplorerWidgetsToExpand = createSelector(
  getEntityExplorerWidgetAncestry,
  (selectedWidgetAncestry: string[]) => {
    return selectedWidgetAncestry.slice(1);
  },
);

export const showWidgetAsSelected = (widgetId: string) => {
  return createSelector(
    getLastSelectedWidget,
    getSelectedWidgets,
    (lastSelectedWidgetId, selectedWidgets) => {
      return (
        lastSelectedWidgetId === widgetId ||
        (selectedWidgets.length > 1 && selectedWidgets.includes(widgetId))
      );
    },
  );
};

export const getFirstSelectedWidgetInList = createSelector(
  getSelectedWidgets,
  (selectedWidgets) => {
    return selectedWidgets?.length ? selectedWidgets[0] : undefined;
  },
);

export const isCurrentWidgetActiveInPropertyPane = (widgetId: string) => {
  return createSelector(
    getIsPropertyPaneVisible,
    getFirstSelectedWidgetInList,
    (isPaneVisible, firstSelectedWidgetId) => {
      return isPaneVisible && firstSelectedWidgetId === widgetId;
    },
  );
};

export const isResizingOrDragging = createSelector(
  (state: DefaultRootState) => state.ui.widgetDragResize.isResizing,
  (state: DefaultRootState) => state.ui.widgetDragResize.isDragging,
  (isResizing, isDragging) => !!isResizing || !!isDragging,
);
// get widgets types associated to a tab
export const getUsedWidgetTypes = createSelector(
  getCanvasWidgets,
  (canvasWidgets) => {
    const widgetTypes = new Set<string>();

    // Iterate through all widgets in the state
    Object.values(canvasWidgets).forEach((widget) => {
      if (widget.type && !widget.type.startsWith("MODULE_WIDGET_")) {
        widgetTypes.add(widget.type);
      }
    });

    return Array.from(widgetTypes);
  },
);
